package org.m8.auth.model;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import org.m8.auth.config.AuthConstants;
import org.m8.util.CollUtil;
import org.m8.util.Snowflake;
import org.m8.util.exception.emnus.GlobalErrorCodeConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

import static org.m8.util.exception.ServiceExceptionUtil.exception0;

/**
 * oauth2 service
 *
 * @author yidanjun
 * @date 2024/9/5
 **/
@Service
public class OAuth2TokenService {
    private static final Snowflake snowflake = Snowflake.build(1, 1, 1);

    @Autowired
    private OAuth2AccessTokenDao oAuth2AccessTokenDao;

    @Autowired
    private OAuth2RefreshTokenDao oAuth2RefreshTokenDao;

    /**
     * 创建accessToken 并入库
     *
     * @param userId 用户id
     * @return accessToken
     */
    public OAuth2AccessTokenDO createAccessToken(String userId){

        OAuth2RefreshTokenDO refreshTokenDO = createoAuth2RefreshToken(userId);

        OAuth2AccessTokenDO accessTokenDO = createoAuth2AccessToken(refreshTokenDO);
        return accessTokenDO;
    }

    private OAuth2AccessTokenDO createoAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO) {
        // accessToken有效期
        DateTime accessTokenExpiresTime = DateUtil.offsetSecond(new Date(), AuthConstants.ACCESS_TOKEN_VALIDITY_SECONDS);
        OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO();
        accessTokenDO.setId(id());
        accessTokenDO.setAccessToken(generateAccessToken());
        accessTokenDO.setUserId(refreshTokenDO.getUserId());
        accessTokenDO.setRefreshToken(refreshTokenDO.getRefreshToken());
        accessTokenDO.setExpiresTime(accessTokenExpiresTime);
        accessTokenDO.setCreateTime(new Date());
        accessTokenDO.setUpdateTime(new Date()) ;
        oAuth2AccessTokenDao.insert(accessTokenDO);
        return accessTokenDO;
    }

    private OAuth2RefreshTokenDO createoAuth2RefreshToken(String userId) {
        // refreshToken有效期
        DateTime refreshTokenExpiresTime = DateUtil.offsetSecond(new Date(), AuthConstants.REFRESH_TOKEN_VALIDITY_SECONDS);
        OAuth2RefreshTokenDO refreshTokenDO = new OAuth2RefreshTokenDO()
                .setId(id())
                .setRefreshToken(generateRefreshToken())
                .setUserId(userId)
                .setExpiresTime(refreshTokenExpiresTime);
        return refreshTokenDO;
    }

    /**
     * 刷新访问令牌
     *
     * @param refreshToken 刷新令牌
     * @return 访问令牌的信息
     */
    public OAuth2AccessTokenDO refreshAccessToken(String refreshToken){
        // 查询访问令牌
        OAuth2RefreshTokenDO oAuth2RefreshTokenDO = oAuth2RefreshTokenDao.getByRefreshToken(refreshToken);
        if (oAuth2RefreshTokenDO == null) {
            throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCodeStr(), "无效的刷新令牌");
        }
        // 移除相关的访问令牌
        List<OAuth2AccessTokenDO> oAuth2AccessTokenDOList = oAuth2AccessTokenDao.getByRefreshToken(refreshToken);
        if (CollUtil.isNotEmpty(oAuth2AccessTokenDOList)){
            oAuth2AccessTokenDao.deleteByIds(oAuth2AccessTokenDOList.stream().map(OAuth2AccessTokenDO::getId).toList());
        }

        // 刷新令牌已过期 删除刷新令牌
        if (new Date().after(oAuth2RefreshTokenDO.getExpiresTime())){
            oAuth2RefreshTokenDao.deleteById(oAuth2RefreshTokenDO.getId());
        }
        // 创建新的accessToken
        return createoAuth2AccessToken(oAuth2RefreshTokenDO);
    }

    /**
     * 获得访问令牌
     *
     *
     * @param accessToken 访问令牌
     * @return 访问令牌的信息
     */
    public OAuth2AccessTokenDO getAccessToken(String accessToken){
        return oAuth2AccessTokenDao.getByAccessToken(accessToken);
    }

    /**
     * 校验访问令牌
     *
     * @param accessToken 访问令牌
     * @return 访问令牌的信息
     */
    public OAuth2AccessTokenDO checkAccessToken(String accessToken){
        OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
        if (accessTokenDO == null) {
            throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCodeStr(), "访问令牌不存在");
        }

        if (new Date().after(accessTokenDO.getExpiresTime())){
            throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCodeStr(), "访问令牌已过期");
        }
        return accessTokenDO;
    }

    /**
     * 移除访问令牌
     * 注意：该流程中，会移除相关的刷新令牌
     *
     *
     * @param accessToken 刷新令牌
     * @return 访问令牌的信息
     */
    public OAuth2AccessTokenDO removeAccessToken(String accessToken){
        OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
        if (accessTokenDO == null) {
            return null;
        }

        oAuth2AccessTokenDao.deleteByAccessToken(accessToken);
        oAuth2RefreshTokenDao.deleteByRefreshToken(accessTokenDO.getRefreshToken());
        return accessTokenDO;
    }

    private static String generateAccessToken() {
        return IdUtil.fastSimpleUUID();
    }

    private static String generateRefreshToken() {
        return IdUtil.fastSimpleUUID();
    }

    private String id() {
        return "OAUTH2_" + snowflake.nextIdStr();
    }
}
